import pygame,sys,math,random
from pygame.locals import *
from rgb_color import *

class MainWindow:
    """ Main Window for pygame.
    Handles event looping, and layer and object management. """

    def __init__ (self, size, color):
        pygame.init()

        self.screen = pygame.display.set_mode(size)
        self.size = size
        self.running = True
        self.color = color
        self.clock = pygame.time.Clock()
        self.objects = {}
        self.layers = [[]] # Will contain lists representing layers

    def getLayers (self):
        """ Returns: (int) number of layers. """

        return len(self.objects)

    def includeObject (self, item, name="sprite", layer=0):
        """ Keys item to name in self.objects,
            Appends item to self.layers """

        layers = self.layers
        self.objects[name] = item
        try:
            layers[layer].append(item)
        except IndexError:
            for i in xrange(layer):
                try: 
                    if layers[i]: pass 
                except IndexError:
                    layers.append([])
            layers[layer-1].append(item)

    def eventLoop (self):
        """ Main event loop to handle user feedback.
        Must include self.gameActions() & self.updateSurface()"""

        obj = self.objects

        while self.running:
            for event in pygame.event.get():
                if event.type == MOUSEBUTTONDOWN:
                    if pygame.mouse.get_pressed()[0]:
                        obj["dude"].following = True
                        obj["dudette"].following = True
                        obj["face"].following = True
                if event.type == MOUSEBUTTONUP:
                    if not pygame.mouse.get_pressed()[0]:
                        obj["dude"].following = False
                        obj["dudette"].following = False
                        obj["face"].following = False
                if event.type == KEYDOWN:
                    if event.key == K_RIGHT: obj["dude"].moving = "right"
                    if event.key == K_LEFT:  obj["dude"].moving = "left"
                    if event.key == K_DOWN:  obj["dude"].moving = "down"
                    if event.key == K_UP:    obj["dude"].moving = "up"
                if event.type == KEYUP:
                    if event.key == K_RIGHT: obj["dude"].moving = False
                    if event.key == K_LEFT:  obj["dude"].moving = False
                    if event.key == K_DOWN:  obj["dude"].moving = False
                    if event.key == K_UP:    obj["dude"].moving = False

                if event.type == QUIT:
                    self.running = False
                    pygame.quit(); sys.exit(); break
            try:
                self.gameActions()
                self.updateSurface()
            except TypeError:
                pass

    def gameActions (self):
        """ This is where to place all major game functions. """

        self.time_passed = self.clock.tick(60)/1000.0
        for thing in self.objects.values():
            try:
                if thing.moving: thing.move()
                if thing.following: thing.follow()
                thing.drift()
                thing.bounce(self.size)
            except AttributeError, e:
                print e

    def updateSurface (self):
        """ Handles all drawing to display. """

        screen = self.screen; blit = screen.blit

        screen.fill(self.color)
        for layer in self.layers:
            for thing in layer:
                thing.draw(screen, blit)
        pygame.display.update()

class ImageCircle(pygame.sprite.Sprite):
    def __init__ (self, speed, size):

        self.image = pygame.image.load("smiley.png").convert_alpha()
        self.xPos =  random.randint(0, size[0]/2)   # Width of screen
        self.yPos = random.randint(0, size[1]/2)    # Height of screen
        self.speed = speed                          # Pixels/second
        self.angle = random.uniform(0, math.pi*2)   # Get a random radian
        self.moving = False
        self.following = False
        size = self.image.get_size()
        self.radias = int(((size[0] + size[1])/2)/2)

    def bounce (self, window_size):
        """ Bounce self if self.xPos or self.yPos is out of range of the game window. """

        if self.xPos > window_size[0] - self.radias:
            self.xPos = 2*(window_size[0] - self.radias) - self.xPos
            self.angle = - self.angle
        elif self.xPos < self.radias:
            self.xPos = 2*self.radias - self.xPos
            self.angle = - self.angle
        if self.yPos > window_size[1] - self.radias:
            self.yPos = 2*(window_size[1] - self.radias) - self.yPos
            self.angle = math.pi - self.angle
        elif self.yPos < self.radias:
            self.yPos = 2*self.radias - self.yPos
            self.angle = math.pi - self.angle


    def drift (self):
        """ Drifts self on a vector at self.angle. """

        self.xPos += math.sin(self.angle) * self.speed/100
        self.yPos -= math.cos(self.angle) * self.speed/100

    def move (self):
        """ Updates position of self via user input. """

        pixels = self.getSpeed()
        if self.moving == "right": self.xPos += pixels
        if self.moving == "left":  self.xPos -= pixels
        if self.moving == "up":    self.yPos -= pixels
        if self.moving == "down":  self.yPos += pixels

    def follow (self):
        """ Updates position of self tracking mouse position. """

        mouse_pos = pygame.mouse.get_pos()
        diff = (self.xPos-mouse_pos[0], self.yPos-mouse_pos[1])
        distance = math.sqrt(diff[0]**2 + diff[1]**2)
        diff_norm = (diff[0]/distance, diff[1]/distance)

        if distance <= 5:
            return
        speed = self.getSpeed()
        self.xPos -= diff_norm[0] * speed
        self.yPos -= diff_norm[1] * speed

    def draw (self, screen, blit):
        """ Blits self to display. """

        blit(self.image, (self.xPos-self.radias, self.yPos-self.radias))

    def getSpeed (self):
        """ Returns: (float) pixels to move for current frame. """

        return (game.time_passed * self.speed)

    def getLayer (self):
        """ Returns: (int) layer self is drawn to. """

        for i,layer in enumerate(game.objects):
            for thing in layer:
                if thing == self:
                    return i


if __name__ == "__main__":

    size = (800,600)
    game = MainWindow(size, BLUE)
    game.includeObject(ImageCircle(300, size), "dude", 0)
    game.includeObject(ImageCircle(100, size), "dudette", 0)
    game.includeObject(ImageCircle(200, size), "face", 0)

    game.eventLoop()
